/**
 * @license
 * 
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @fileoverview Blockly Angular Component.
 * @author samelh@google.com (Sam El-Husseini)
 */
 import * as jsg from 'blockly/javascript';
import { Component, OnInit } from '@angular/core';
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';

import * as Blockly from 'blockly';
//讯飞api
import * as CryptoJS from 'crypto-js';
import { templateJitUrl } from '@angular/compiler';

const config = {
  // 请求地址
  hostUrl: "http://raasr.xfyun.cn/api/",
  // 在控制台-我的应用-语音转写获取
  appId: "e58cab55",
  // 在控制台-我的应用-语音转写获取
  secretKey: "bd2437c1cd037505bbbb2671fefaf8d6",
  // 0464452c0e1644cd2473551e78a1a495
  // secretKey: "0464452c0e1644cd2473551e78a1a495",

  // 音频文件地址
  filePath: "/Users/xqr/Desktop/测试.m4a"
  // filePath: "./test.mp3"
}

// 请求的接口名，接口调用为以下五步
const api = {
  prepare: 'prepare',
  upload: 'upload',
  merge: 'merge',
  getProgress: 'getProgress',
  getResult: 'getResult'
}


// 文件分片大小 10M
const FILE_PIECE_SICE = 10485760
//5k 用于测试分片功能
// const FILE_PIECE_SICE = 5 * 1024;

// ——————————————————转写可配置参数————————————————
// 参数可在官网界面（https://doc.xfyun.cn/rest_api/%E8%AF%AD%E9%9F%B3%E8%BD%AC%E5%86%99.html）查看，根据需求可自行在gene_params方法里添加修改
// 转写类型
let lfasr_type = 0
// 是否开启分词
let has_participle = 'false'
let has_seperate = 'true'
// 多候选词个数
let max_alternatives = 0


//获取鉴权签名，用来加密数据
function getSigna(ts: string): string {
  let md5 = CryptoJS.MD5(config.appId + ts).toString()
  let sha1 = CryptoJS.HmacSHA1(md5, config.secretKey)
  let signa = CryptoJS.enc.Base64.stringify(sha1)
  return signa
}
//sliceid生成器
class SliceIdGenerator {
  constructor() {
    this.__ch = 'aaaaaaaaa`';
  }
  private __ch: string;
  getNextSliceId(): string {
    let ch = this.__ch
    let i = ch.length - 1
    while (i >= 0) {
      let ci = ch[i]
      if (ci !== 'z') {
        ch = ch.slice(0, i) + String.fromCharCode(ci.charCodeAt(0) + 1) + ch.slice(i + 1)
        break
      } else {
        ch = ch.slice(0, i) + 'a' + ch.slice(i + 1)
        i--
      }
    }
    this.__ch = ch
    return this.__ch
  }
}

class RequestApi {
  //存放从讯飞获取的id号
  private appId: string;
  private fileLen: number;
  private fileName: string;
  private file: any;
  constructor(appId: string, file: any, private http: HttpClient) {
    this.appId = appId;
    this.fileLen = file.size;
    this.fileName = file.name;
    this.file = file;
  }
  geneParams(apiName: string, taskId?: string, sliceId?: number | string) {
    // 获取当前时间戳
    let ts = Math.floor(new Date().getTime() / 1000);
    var paramDict = new Map();//使用map来存放键值对
    paramDict.set("app_id", this.appId);
    paramDict.set("signa", getSigna(ts.toString()));
    paramDict.set("ts", ts);
    //调用接口五步
    switch (apiName) {
      case api.prepare:
        let sliceNum = Math.ceil(this.fileLen / FILE_PIECE_SICE);
        paramDict.set("file_len", this.fileLen);
        paramDict.set("file_name", this.fileName);
        paramDict.set("slice_num", sliceNum);
        break;
      case api.upload:
        paramDict.set("task_id", taskId);
        paramDict.set("slice_id", sliceId);
        break;
      case api.merge:
        paramDict.set("task_id", taskId);
        paramDict.set("file_name", this.fileName);
        break;
      case api.getProgress:
      case api.getResult:
        paramDict.set("task_id", taskId);
        break;
    }
    return paramDict;
  }

  // 同步获取
  async geneRequest(apiName: string, data?: any, file?: any, badApiFlag?: boolean) {
    var headOptions: any;
    const url = "/api/" + apiName;
    var body: any;
    if (file) {
      if (badApiFlag) {
        var blob = file.slice(0, this.fileLen);
        data.append("content", blob, this.fileName);
        // console.log("file type:", typeof blob);
        body = data;
      } else {
        //下面的用于带分片功能的
        data.append("content", file, this.fileName);
        body = data;
      }

    } else {
      //处理不带文件的请求
      headOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }) };
      body = data;
    }
    try {
      //同步传输数据
      let res: any = await this.http.post(url, body, headOptions).toPromise();
      // console.log(apiName, res);
      if (res.ok == 0) {
        //sucess
      } else {
        //err
      }
      return res;
    } catch (err) {
      // console.log(apiName + "error" + err);
    }
  }

  prepareRequest() {//返回json格式的状态信息
    return this.geneRequest(api.prepare, this.geneFormatData(this.geneParams(api.prepare)));
  }
  //这个接口不太行，只能不分片上传一个小的文件
  uploadRequestBad(taskId: string) {
    let self = this;

    var sig = new SliceIdGenerator();

    return self.geneRequest(api.upload, self.geneFormData(self.geneParams(api.upload, taskId, sig.getNextSliceId())), self.file, true);

  }
  //尝试对上述问题进行修正
  uploadRequest(taskId: string) {
    let self = this;

    return new Promise((resolve, reject) => {
      var sig = new SliceIdGenerator();

      let index = 1, start = 0;

      async function loopUpload() {
        let len = self.fileLen < FILE_PIECE_SICE ? self.fileLen : FILE_PIECE_SICE;
        let end = start + len;

        var blob = self.file.slice(start, end);

        let res = await self.geneRequest(api.upload, self.geneFormData(self.geneParams(api.upload, taskId, sig.getNextSliceId())), blob, false);

        if (res.ok == 0) {
          // console.log("upload slice " + index + "success!");
          index++;
          start = end;
          self.fileLen -= len;
          if (self.fileLen > 0) {
            //没传完继续递归
            loopUpload();
          } else {
            //传完就调用resolve退出
            resolve(null);
          }
        }
      }
      loopUpload();
    })
  }
  mergeRequest(taskId) {
    return this.geneRequest(api.merge, this.geneFormatData(this.geneParams(api.merge, taskId)))
  }

  getProgressRequest(taskId) {
    let self = this

    return new Promise((resolve, reject) => {
      function sleep(time) {
        return new Promise((resolve) => {
          setTimeout(resolve, time)
        });
      }

      async function loopGetProgress() {
        let res = await self.geneRequest(api.getProgress, self.geneFormatData(self.geneParams(api.getProgress, taskId)))

        let data = JSON.parse(res.data)
        let taskStatus = data.status
        // log.info('task ' + taskId + ' is in processing, task status ' + taskStatus)
        if (taskStatus == 9) {
          // log.info('task ' + taskId + ' finished')
          resolve(null)
        } else {
          sleep(20000).then(() => loopGetProgress())
        }
      }

      loopGetProgress()
    })
  }

  async getResultRequest(taskId) {
    let res = await this.geneRequest(api.getResult, this.geneFormatData(this.geneParams(api.getResult, taskId)))

    let data = JSON.parse(res.data),
      result = ''
    data.forEach(val => {
      result += val.onebest
    })
    // console.log(result)
    return result;
  }

  async allApiRequest() {
    try {
      let prepare = await this.prepareRequest()
      let taskId = prepare.data
      await this.uploadRequest(taskId)
      await this.mergeRequest(taskId)
      await this.getProgressRequest(taskId)
      let res=await this.getResultRequest(taskId)
      // var res=await this.getResultRequest(taskId);
      // console.log(res);
      return res;
    } catch (err) {
     
      // log.error(err)
      // return getProgressRequest();
    }
   
  }

  //把map中的键值对转换成可供http发送的application/x-www-form-urlencoded表单格式字符串
  geneFormatData(map: Map<string, any>): string {
    var form = new URLSearchParams();
    for (let [key, value] of map) {
      let valueStr: string = value;
      if (typeof value === "number") {
        valueStr = value.toString();
      }
      form.set(key, valueStr);
    }
    // console.log(form);
    return form.toString();
  }
  //转换成FormData的表单格式
  geneFormData(map: Map<string, any>): any {
    var form = new FormData();
    for (let [key, value] of map) {
      let valueStr: string = value;
      if (typeof value === "number") {
        valueStr = value.toString();
      }
      form.append(key, valueStr);
      // console.log(key + ":" + valueStr);
    }
    return form;
  }
}

@Component({
  selector: 'app-blockly',
  templateUrl: './blockly.component.html',
  styleUrls: ['./blockly.component.css']
})
//通过这个类可以将该类中的函数传递出去
export class BlocklyComponent implements OnInit {

  constructor(private http: HttpClient) { }
  private file: any;
  aaa:any;
  //angular 框架中初始化
  ngOnInit() {
    const blocklyDiv = document.getElementById('blocklyDiv');
    // var code = Blockly.JavaScript.workspaceToCode(demoWorkspace);

   //大块
    Blockly.Blocks['voice_detect'] = {
      init: function () {
        this.appendValueInput("location")
          .setCheck("")
          .appendField("请输入您要翻译的语音文件的位置");
        this.appendValueInput("APPID")
          .setCheck(null)
          .setAlign(Blockly.ALIGN_RIGHT)
          .appendField("输入APPID");
        this.appendValueInput("APIkey")
          .setCheck(null)
          .setAlign(Blockly.ALIGN_RIGHT)
          .appendField(new Blockly.FieldLabelSerializable("输入APIkey"), "NAME");
        this.appendValueInput("URL")
          .setCheck(null)
          .setAlign(Blockly.ALIGN_RIGHT)
          .appendField("输入url网址");
        this.setColour(20);
        this.setTooltip("");
        this.setHelpUrl("");
      }
    };
    jsg['voice_detect'] = function (block) {
      // 获取各个块的数值
      var value_name = jsg.valueToCode(block, 'location', jsg.ORDER_ATOMIC);
      // console.log(value_name);
      var value_appid = jsg.valueToCode(block, 'APPID', jsg.ORDER_ATOMIC);
      var value_apikey = jsg.valueToCode(block, 'APIkey', jsg.ORDER_ATOMIC);
      var value_url = jsg.valueToCode(block, 'URL', jsg.ORDER_ATOMIC);
      // TODO: Assemble JavaScript into code variable.
    
      var code = (value_url == "(#url)" && value_name=="(#location)" && value_appid=="(#AppId)" && value_apikey=="(#Api_Key)")
     
      if (code) {
        return "yes";
      } else {
        return "有错误！请检查您的连接块！";
      }
    };

// url输入测试块
    Blockly.Blocks['input_url'] = {
      init: function () {
        this.appendDummyInput()
          .appendField(new Blockly.FieldTextInput("http://raasr.xfyun.cn/api/"), "NAME");
        this.setOutput(true, null);
        this.setColour(180);
        this.setTooltip("");
        this.setHelpUrl("");
      }
    };
    jsg['input_url'] = function (block) {
      var text_name = block.getFieldValue('NAME');
      // TODO: Assemble JavaScript into code variable.
      var code = '#url';
      // TODO: Change ORDER_NONE to the correct strength.
      return [code, jsg.ORDER_NONE];
    };

// 文件位置输入测试块
Blockly.Blocks['file_location'] = {
  init: function () {
    this.appendDummyInput()
      .appendField(new Blockly.FieldTextInput("/Users/xqr/Desktop/测试.m4a"), "NAME");
    this.setOutput(true, null);
    this.setColour(200);
    this.setTooltip("");
    this.setHelpUrl("");
  }
};
jsg['file_location'] = function (block) {
  var text_name = block.getFieldValue('NAME');
  // TODO: Assemble JavaScript into code variable.
  var code ='#location';
  // TODO: Change ORDER_NONE to the correct strength.
  return [code, jsg.ORDER_NONE];
};

// APPID输入测试块
Blockly.Blocks['APPID'] = {
  init: function () {
    this.appendDummyInput()
      .appendField(new Blockly.FieldTextInput("e58cab55"), "NAME");
    this.setOutput(true, null);
    this.setColour(220);
    this.setTooltip("");
    this.setHelpUrl("");
  }
};
jsg['APPID'] = function (block) {
  var text_name = block.getFieldValue('NAME');
  // TODO: Assemble JavaScript into code variable.
  var code ='#AppId';
  // TODO: Change ORDER_NONE to the correct strength.
  return [code, jsg.ORDER_NONE];
};

// APIKEY测试块
Blockly.Blocks['APIKEY'] = {
  init: function () {
    this.appendDummyInput()
      .appendField(new Blockly.FieldTextInput("bd2437c1cd037505bbbb2671fefaf8d6"), "NAME");
    this.setOutput(true, null);
    this.setColour(240);
    this.setTooltip("");
    this.setHelpUrl("");
  }
};
jsg['APIKEY'] = function (block) {
  var text_name = block.getFieldValue('NAME');
  // TODO: Assemble JavaScript into code variable.
  var code ='#Api_Key';
  // TODO: Change ORDER_NONE to the correct strength.
  return [code, jsg.ORDER_NONE];
};


    this.aaa=Blockly.inject(blocklyDiv, {
      readOnly: false,
      media: 'media/',
      trashcan: true,
      move: {
        scrollbars: true,
        drag: true,
        wheel: true
      },
      toolbox: `
      <xml xmlns="https://developers.google.com/blockly/xml" id="toolbox-simple" style="display: none"> 
      <category name="语音识别测试" colour="%{BKY_MATH_HUE}">
        <block type="voice_detect"></block>
        <block type="input_url"></block>
        <block type="file_location"></block>
        <block type="APPID"></block>
        <block type="APIKEY"></block>
      </category>
      </xml>`
    } as Blockly.BlocklyOptions);
  }

  handleFileSelect(evt: any) {
    // console.log("evt", evt);
    var files = evt.target.files;
    var f = files[0];
    this.file = f;
  }

  submitButton() {
    // console.log("提交按钮被触发!");

    //用法new一个Request出来
    
    let ra=new RequestApi(config.appId, this.file, this.http);
    ra.allApiRequest();

    return null;
  }
  runButton(){
    var code = jsg.workspaceToCode(this.aaa);
    console.log(code);
    if(code.match("yes")){
      // console.log('111');
      let ra=new RequestApi(config.appId, this.file, this.http);
      let res=ra.allApiRequest();
      res.then(this.solveRes);
      return null;
    }

    jsg.INFINITE_LOOP_TRAP = null;
    try {
      window.alert(code);
      // eval(code);
      // eval()可以让写在函数里的代码运行在全局作用域中。
    }
    catch (e) {
      alert(e);
    }
  }
  //回调函数
  solveRes(data:any){
      // console.log("type",typeof data);
      window.alert(data);
  }

}
